package com.bjian.CodeSandBox.codeSandbox.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.bjian.CodeSandBox.codeSandbox.CodeSandbox;
import com.bjian.CodeSandBox.codeSandbox.model.ExecuteCodeRequest;
import com.bjian.CodeSandBox.codeSandbox.model.ExecuteCodeResponse;
import com.bjian.CodeSandBox.codeSandbox.model.JudgeInfo;
import com.bjian.CodeSandBox.unitl.ProcessUtil;
import com.bjian.CodeSandBox.unitl.model.ExeuteMessage;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 代码沙箱实现例子
 */
public class ExampleCodeSandboxImplOld implements CodeSandbox {

    private final static Long TIME_OUT = 5000L;

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        if (executeCodeRequest == null) {
            throw new RuntimeException();
        }
        String code = executeCodeRequest.getCode();
        List<String> inputList = executeCodeRequest.getInputList();
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        try {
            //1.把用户代码保存为代码文件
            //todo 把代码字符串保存为文件编译前 使用字典树（wordTree）校验一遍是否包含危险代码（暂未实现）
            String userDIR = System.getProperty("user.dir");//读取用户路径
            String globalCodePathName = userDIR + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "tmpCodeDir";

            if (!FileUtil.isFile(globalCodePathName)) {
                FileUtil.mkdir(globalCodePathName);
            }

            String userCodeParentPathName = globalCodePathName + File.separator + UUID.randomUUID();
            String userCodePath = userCodeParentPathName + File.separator + "Main.java";
            File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
//            System.out.println(userCodeFile.getAbsolutePath());
            String compiledCmd = String.format("javac -encoding utf-8 %s", userCodeFile.getAbsolutePath());//组合最终要运行的编译命令
            //2.编译代码，得到class文件
            Process process = Runtime.getRuntime().exec(compiledCmd);
            ExeuteMessage exeuteMessage = ProcessUtil.runProcessAndGetMessage(process, "编译");
//            System.out.println(exeuteMessage);
            //3.执行代码，得到运行结果
            List<ExeuteMessage> exeuteMessageList = new ArrayList<>();

            for (String inputArgs : inputList) {// 循环执行，每组数据运行一次
                String runCmd = String.format("java -Dfire.encoding=UTF-8 -cp %s Main %s", userCodeParentPathName, inputArgs);
                Process runProcess = Runtime.getRuntime().exec(runCmd);
                new Thread(() -> {//运行计时线程，当超过设定最大执行时间则中断运行代码的线程（该实现较为简易，可使用Future和ExecutorService接口更为完整的控制）
                    try {
                        Thread.sleep(TIME_OUT);
                        if (runProcess.isAlive()) {
                            runProcess.destroy();
                            System.out.println("超时了，中断");
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).start();
                ExeuteMessage runExeuteMessage = ProcessUtil.runProcessAndGetMessage(runProcess, "运行");
                exeuteMessageList.add(runExeuteMessage);
            }
            //4.收集整理运行结果
            List<String> outputList = new ArrayList<>();
            long maxTime = Long.MIN_VALUE;
            for (ExeuteMessage RunExeuteMessage : exeuteMessageList) {
                String errorMessage = RunExeuteMessage.getErrorMessage();
                if (StrUtil.isNotBlank(errorMessage)) {
                    executeCodeResponse.setMessage(errorMessage);
                    // 用户提交的代码执行中存在错误
                    executeCodeResponse.setStatus(3);
                    break;
                }
                //统计运行时间，选择最大运行时间保存
                Long runTime = RunExeuteMessage.getRunTime();
                maxTime = Math.max(maxTime, runTime);
                String OutputResult = RunExeuteMessage.getMessage();
                outputList.add(OutputResult);
            }
            JudgeInfo judgeInfo = new JudgeInfo();
            judgeInfo.setTime(maxTime);
            executeCodeResponse.setJudgeInfo(judgeInfo);
            executeCodeResponse.setOutputList(outputList);

            //5.文件清理，释放空间
            if (userCodeFile.getParentFile() != null) {
                boolean del = FileUtil.del(userCodeParentPathName);
                System.out.println("删除" + (del ? "成功" : "失败"));
            }
            //6.错误处理
        } catch (IOException e) {
            ExecuteCodeResponse errorExecuteCodeResponse = getErrorExecuteCodeResponse(e);
            return errorExecuteCodeResponse;
        }
        return executeCodeResponse;
    }


    /**
     * 用于出现异常时，能够正常返回数据。
     * 包装一个用于出现异常时返回的特殊对象
     * @param throwable 异常信息
     * @return ExecuteCodeResponse 包含异常信息的沙箱运行结果对象
     */
    private ExecuteCodeResponse getErrorExecuteCodeResponse(Throwable throwable) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutputList(new ArrayList<>());
        executeCodeResponse.setMessage(throwable.getMessage());
        executeCodeResponse.setStatus(2);//表示沙箱代码错误
        executeCodeResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeResponse;
    }

}
